// system.cc 
//	Nachos initialization and cleanup routines.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"

// This defines *all* of the global data structures used by Nachos.
// These are all initialized and de-allocated by this file.

Thread *currentThread;			// the thread we are running now
Thread *threadToBeDestroyed;  		// the thread that just finished
Scheduler *scheduler;			// the ready list
Interrupt *interrupt;			// interrupt status
Statistics *stats;			// performance metrics
Timer *timer;				// the hardware timer device,
					// for invoking context switches

#ifdef FILESYS_NEEDED
FileSystem  *fileSystem;
#endif

#ifdef FILESYS
SynchDisk   *synchDisk;
#endif

#ifdef USER_PROGRAM	// requires either FILESYS or FILESYS_STUB
Machine *machine;	// user program memory and registers
KernelLock LockTable[MAXLOCKNUM];
Lock* LockTableLock;
KernelCV CVTable[MAXCVNUM];
Lock* CVTableLock;



//Project2 part2
BitMap* PhysMap;
Lock* PhysMapLock;

Lock* ProcessTableLock;
ProcessTable* procTable;
//Project 3
int tlbCounter;
IPTEntry* ipt;
Lock * iptLock;
int pageReplaceType;
OpenFile *swapFile;	//to put in dirty physical memory
BitMap *swapMap;	//to allocate the content in swap
Lock *swapLock;
List *ppnFIFOList;		//for pageFaultHandler physical memory full physical memory page FIFO evict implement
//----------------------------------------------------------------------
// ProcessTable::ProcessTable
//	constructor of ProcessTable class
// 	Initialize all the private variables
//----------------------------------------------------------------------
ProcessTable::ProcessTable(int itemNum){
	int i;
	maxItemNum = itemNum;
	processCounter = 0;
	entries = new processTableEntry[maxItemNum];
	for(i=0;i<maxItemNum;i++){
		entries[i].name = NULL;
		entries[i].space = NULL;
		entries[i].childProcessCounter = 0;
		entries[i].spaceId = -1;
		entries[i].threadIdMap = new BitMap(MAXFORKNUM);
		entries[i].fixedPageEnd = -1;
		entries[i].threadCounter = 0;
		entries[i].threadList = new List;
	}
	DEBUG('a',"Initializing a ProcessTable with maximum %d items.\n", itemNum);

}
//----------------------------------------------------------------------
// ProcessTable::ProcessTable
//	deallocate ProcessTable
// 	Initialize all the private variables
//----------------------------------------------------------------------
ProcessTable::~ProcessTable(){
	delete entries;
}

//----------------------------------------------------------------------
// ProcessTable::addProcess
// 	add a process entry to the process table  
//	return -1 if add failed
//	return the threadId of the main thread in the created Process if succeeded
//	"debugName" the name of the process to be add
//	"len" the length of the debugName
//	"s" the AddrSpace of the added process
//	"id" the spaceId of the added process
//----------------------------------------------------------------------
int 
ProcessTable::addProcess(char *debugName, int len, AddrSpace *s, SpaceId id, Thread *t){
	if(id <0||id>maxItemNum) return -1;	//invalid id to addProcess
	if(s == NULL) return -1;
	entries[id].name = debugName;
	entries[id].name[len] = '\0';
	entries[id].space = s;
	entries[id].spaceId = id;
	entries[id].threadCounter = 1;
	entries[id].threadList->Append((void*)t);
	entries[id].fixedPageEnd = s->getnumPages()-divRoundUp(UserStackSize,PageSize)-1;
	if(isThreadFull(id) == true) return -1;
	t->threadId = entries[id].threadIdMap->Find();
	entries[currentThread->spaceId].childProcessCounter++;
	processCounter++;
	DEBUG('a',"AddProcess with spaceId %d and kernel thread with threadid %d.\n", id, t->threadId);

	return t->threadId;
}

int 
ProcessTable::addProcess(char *debugName, int len, AddrSpace *s, SpaceId id){
	if(id <0||id>maxItemNum) return -1;	//invalid id to addProcess
	if(s == NULL) return -1;
	entries[id].name = debugName;
	entries[id].name[len] = '\0';
	entries[id].space = s;
	entries[id].spaceId = id;
	entries[id].threadCounter = 1;
	if(isThreadFull(id) == true) return -1;
	currentThread->threadId = entries[id].threadIdMap->Find();
	entries[id].threadList->Append((void*)currentThread);
	entries[id].fixedPageEnd = s->getnumPages()-divRoundUp(UserStackSize,PageSize)-1;

	entries[id].childProcessCounter++;
	processCounter++;
	DEBUG('a',"AddProcess with spaceId %d and kernel thread with threadid %d.\n", id, currentThread->threadId);

	return currentThread->threadId;


}

//----------------------------------------------------------------------
// ProcessTable::addThread  
//	return the thread that has been added to the threadList
//	return NULL if add failed
//	"id" the spaceId of the added thread
//----------------------------------------------------------------------
Thread* 
ProcessTable::addThread(SpaceId id){
	Thread *t;
	int needPage, stackStart, stackEnd;
	int i;
	int physid;
	
	if(isValidId(id) == false) {
		printf("DEBUG: addThread error\n");
		return NULL;
	}
	if(isThreadFull(id) == true){
		printf("Error: Too many fork()!\n");
		return NULL;
	}
	
	needPage = divRoundUp(UserStackSize, PageSize);
	PhysMapLock->Acquire();
/*	if(PhysMap->NumClear()<needPage){
		printf("Error: Not enough physical memory for a new thread!\n");
		PhysMapLock->Release();
		return NULL;
	}
*/
	t = new Thread("kernel_thread");
	t->threadId = entries[id].threadIdMap->Find();
	if(t->threadId == -1){
		printf("ERROR: threadID pool full!\n");
		return NULL;
	}
	stackStart = entries[id].fixedPageEnd + needPage*(t->threadId) + 1;
	stackEnd = stackStart + needPage;
	entries[id].space->pageTableLock->Acquire();
	for(i=stackStart;i<stackEnd;i++){
		(entries[id].space->getPageTable())[i].virtualPage = i;
		(entries[id].space->getPageTable())[i].physicalPage = -1;
		/*(entries[id].space->getPageTable())[i].physicalPage = PhysMap->Find();
		if((entries[id].space->getPageTable())[i].physicalPage == -1){
			printf("Error: Fail to find any free physical page when addThread for process with spaceId %d.\n",id);
		}*/
		(entries[id].space->getPageTable())[i].valid = false;
		(entries[id].space->getPageTable())[i].use = false;
		(entries[id].space->getPageTable())[i].dirty = false;
		(entries[id].space->getPageTable())[i].readOnly = false;
		//printf("myDEBUG AddThread:  The location of vpn %d is %d; thread ID is %d\n",(entries[id].space->getPageTable())[i].virtualPage,(entries[id].space->getPageTable())[i].diskLocation, t->threadId);
		(entries[id].space->getPageTable())[i].diskLocation = NOTONDISK;

		(entries[id].space->getPageTable())[i].byteOffset = -1;
		(entries[id].space->getPageTable())[i].processId = id;
		/*
		//modify ipt
		physid = (entries[id].space->getPageTable())[i].physicalPage;
		//printf("\nAllocating stack physical page %x to virtual page %x to process with id %d", physid, i, id);
		iptLock->Acquire();
		ipt[physid].virtualPage = i;
		ipt[physid].physicalPage = physid;
		ipt[physid].valid = true;
		ipt[physid].use = false;
		ipt[physid].dirty = false;
		ipt[physid].readOnly = false;
		ipt[physid].processId = id;
		ipt[physid].space = entries[id].space;
		iptLock->Release();
		
		DEBUG('a', "Initializing stack segment, at 0x%x for virtual page %x, size %d\n", (entries[id].space->getPageTable())[i].physicalPage,i, PageSize);
		*/
	}
	
	entries[id].space->pageTableLock->Release();
	PhysMapLock->Release();
	entries[id].threadCounter++;
	entries[id].threadList->Append((void*)t);
	t->spaceId = id;
	t->space = entries[id].space;
	DEBUG('a',"Addthread with thread id %d and spaceID %d\n",t->threadId, id);
	return t;
}

//----------------------------------------------------------------------
// ProcessTable::deleteThread  
//	return the threadId that has been deleted
//	return -1 if delete failed
//	"id" the spaceId of the deleted thread
//	"t" is the thread pointer of the thread to be deleted
//----------------------------------------------------------------------

int 
ProcessTable::deleteThread(SpaceId id, Thread *t){
	if(isValidId(id) == false) {
		printf("DEBUG:deleteThread error");
		return -1;
	}
	if((entries[id].threadList->RemoveIndicatedOne((void*)t)) == -1){
		printf("Error: No such thread when trying to delete thread!\n");
		return -1;
	}
	DEBUG('a',"current thread with thread id %d to delete a thread with thread id %d and space id %d\n", currentThread->threadId, t->threadId, id);
	entries[id].threadCounter--;
	entries[id].threadIdMap->Clear(t->threadId);
	//printf("DEBUG: Clearing threadID %d\n",t->threadId);
//	if(isLastThread(id) == 1){
//		deleteProcess(id);
//	}else{
		updatePhysMap(id, false);
//	}
	DEBUG('a',"deleteThread with thread id %d and space Id %d\n", t->threadId, id);
	return t->threadId;
}

//----------------------------------------------------------------------
// ProcessTable::deleteProcess  
//	return the spaceId that has been deleted
//	return -1 if delete failed
//	"id" the spaceId of the deleted process
//----------------------------------------------------------------------

int 
ProcessTable::deleteProcess(SpaceId id){
	if(isValidId(id) == false){
		printf("DEBUG: delete process error\n");
		return -1;
	}
	updatePhysMap(id, true);
	//delete entries[id].space->currentExecutable;
	delete entries[id].space;
	delete entries[id].threadList;
	delete entries[id].threadIdMap;
	entries[id].name = NULL;
	entries[id].space = NULL;
	entries[id].childProcessCounter = 0;
	entries[id].spaceId = -1;
	entries[id].threadIdMap = new BitMap(MAXFORKNUM);
	entries[id].fixedPageEnd = -1;
	entries[id].threadCounter = 0;
	entries[id].threadList = new List;
	processCounter --;
	DEBUG('a',"delete process with space id %d.\n", id);
	return id;
}
//----------------------------------------------------------------------
// ProcessTable::updatePhysMap  
//	"id" the spaceId of process of the physical pages to be deleted
//	"deleteProc" identifies whether this update physical page is for
//	process delete
//----------------------------------------------------------------------

void 
ProcessTable::updatePhysMap(SpaceId id, bool deleteProc){
	int delePageNum, pageStart, pageEnd;
	int i, j;
	int physid;
	//IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
    if(deleteProc == true){ //delete everything associate with the process
		entries[id].space->pageTableLock->Acquire();
            
        DEBUG('a',"delete fixed pages total number: %d\n", entries[id].space->getmaxnumPages());
		delePageNum = entries[id].space->getmaxnumPages();
            
        for(i=0;i<delePageNum;i++){
			if((entries[id].space)->getPageTable()[i].valid == true) {
//            	DEBUG('a',"Clear physical page %d for process with space id %d\n", (entries[id].space)->getPageTable()[i].physicalPage, id);
				physid = (entries[id].space->getPageTable())[i].physicalPage;
					//invalidate ipt entry
				iptLock->Acquire();
                ipt[physid].valid = false;      //invalidate ipt
				ipt[physid].use = false;		//make sure this page will be replaced
				iptLock->Release();
				printf("\ntangDEBUG: Exit Clear ipt[%d] virtual page %d for process with space id %d thread id%d\n", physid, i, id, currentThread->threadId);

                PhysMapLock->Acquire();
                PhysMap->Clear(((entries[id].space)->getPageTable())[i].physicalPage);
				PhysMapLock->Release();
					
				IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts	
				for(int k=0;k<TLBSize;k++){
					if((machine->tlb[k].valid == true) && (machine->tlb[k].physicalPage == (entries[id].space)->getPageTable()[i].physicalPage)){
						machine->tlb[k].valid = false;	//invalidate tlb entry
						printf("\ntangDebug: In Exit: clear TLB[%d] by process with id %d thread with id %d\n", k, currentThread->spaceId, currentThread->threadId);
					}
				}
				(void) interrupt->SetLevel(oldLevel);	//restore interrupts and return						
			}else if((entries[id].space)->getPageTable()[i].diskLocation == SWAPFILE){
					swapLock->Acquire();
					swapMap->Clear(((entries[id].space->getPageTable())[i].byteOffset)/PageSize);
					printf("\ntangDEBUG: Exit Clear swap %d for virtual page %d for process with space id %d thread id%d\n", ((entries[id].space->getPageTable())[i].byteOffset)/PageSize, i, id, currentThread->threadId);
					swapLock->Release();
				}
        }
            
        entries[id].space->pageTableLock->Release();
        DEBUG('a',"Clear resources of process with spaceId with %d.\n", id);
    }else{  //free the stack associate with the thread
		delePageNum = divRoundUp(UserStackSize, PageSize);
        pageStart = entries[id].fixedPageEnd + delePageNum*(currentThread->threadId) + 1;
        pageEnd = pageStart + delePageNum;
        DEBUG('a',"thread with threadid %d is to delete virtual page from 0x%x to 0x%x\n",currentThread->threadId, pageStart, pageEnd);
            
		entries[id].space->pageTableLock->Acquire();
        for(j = pageStart;j < pageEnd; j++){
		//printf("myDEBUG: Exit page table entry %d 's valid bit is %d with spaceId%d\n",j,(entries[id].space)->getPageTable()[j].valid, id);
			if((entries[id].space)->getPageTable()[j].valid == true) {
				DEBUG('a',"Clear physical page %d for virtual page %x thread with thread id %d and space id %d\n", (entries[id].space->getPageTable())[j].physicalPage,j,currentThread->threadId, id);
                physid = (entries[id].space->getPageTable())[j].physicalPage;
					
				iptLock->Acquire();
				ipt[physid].valid = false;      //invalidate ipt
				ipt[physid].use = false;		//make sure this page will be replaced
				iptLock->Release();
				printf("tangDEBUG: Exit Clear ipt[%d] for virtual page %d thread with thread id %d and space id %d \n", physid,j,currentThread->threadId, id);
				PhysMapLock->Acquire();
				PhysMap->Clear(physid);
				PhysMapLock->Release();
				
				IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts	
				for(int k=0;k<TLBSize;k++){
					if((machine->tlb[k].valid == true) && (machine->tlb[k].physicalPage == (entries[id].space)->getPageTable()[j].physicalPage)){
						machine->tlb[k].valid = false;	//invalidate tlb entry
						printf("\ntangDebug: In Exit: clear TLB[%d] by process with id %d thread with id %d", k, currentThread->spaceId, currentThread->threadId);

					}
				}
				(void) interrupt->SetLevel(oldLevel);	//restore interrupts and return	
            }
			else if((entries[id].space)->getPageTable()[j].diskLocation == SWAPFILE){
					swapLock->Acquire();
					swapMap->Clear(((entries[id].space->getPageTable())[j].byteOffset)/PageSize);
					printf("\ntangDEBUG: Exit Clear swap %d for virtual page %d for process with space id %d thread id%d\n", ((entries[id].space->getPageTable())[j].byteOffset)/PageSize, j, id, currentThread->threadId);
					swapLock->Release();
				}
			}
			entries[id].space->pageTableLock->Release();
		//test
		/*
		for(j = 0;j <= 30; j++){
			if((entries[id].space)->getPageTable()[j].valid == true)
				printf("DEBUG:entry %d is valid with physicalpage %x\n",j,(entries[id].space)->getPageTable()[j].physicalPage);
				else printf("DEBUG:entry %d is invalid \n",j);
		}
		*/
		//end of test	
		DEBUG('a',"Free the stack of thread with thread id %d and spaceId with %d. \n",currentThread->threadId, id);
		
	}
	//(void) interrupt->SetLevel(oldLevel);	//restore interrupts and return
}
//----------------------------------------------------------------------
// ProcessTable::isLastThread
// 	check whether the currentThread is the last executing(not sleeping)
//	thread.  
// 	return 1 if it is last
//	return 0 if it is not the last
// 	return -1 if this id is invalid
//	"id" the spaceId to be checked.
//----------------------------------------------------------------------
int
ProcessTable::isLastThread(SpaceId id){
	int activeThreadCounter = 0;
	int i = 0;
	if(isValidId(id) == false) {
		DEBUG('a',"DEBUG: ERROR SpaceId: %d\n", id);
		printf("DEBUG: isLastThread error\n");
		return -1;
	}
	ListElement *p = entries[id].threadList->getFirstItem();
	for(;p!=NULL;p=p->next){
		if((((Thread*)p->item)->isUserActive()) == true){
			activeThreadCounter++;
		}
		i++;
		DEBUG('a',"Thread with threadid %d Check %dth thread, active thread number is %d \n",currentThread->threadId, i, activeThreadCounter);
	}
	if(activeThreadCounter == 1) return 1;
		else return 0;

}
//----------------------------------------------------------------------
// ProcessTable::isValidId
// 	check whether the id is valid.
//	Invalid ID are those have been deleted or out of range id.  
// 
//	"id" the spaceId to be checked.
//----------------------------------------------------------------------
bool
ProcessTable::isValidId(SpaceId id){
	if((id<0)||(id>maxItemNum)){
		printf("Error:SpaceId out of range in ProcessTable::isValidId\n");
		return false;
	}
	if(entries[id].space == NULL){
		DEBUG('a',"DEBUG: SpaceId: %d.\n",id);
		printf("Error:requested process entry has been deleted or uninitialized.\n");
		return false;
	}
	return true;
}
//----------------------------------------------------------------------
// ProcessTable::FindSpaceId
// 	find an available space id/empty entry in process table   
// 
//	return the space id
//----------------------------------------------------------------------
SpaceId 
ProcessTable::FindSpaceId(){
	int i = 0;
	for(;i<maxItemNum;i++){
		if(entries[i].space == NULL)
			return i;
	}
	return -1;
}

ListElement*
List::getFirstItem(){
	return first;
}
#endif

#ifdef NETWORK
PostOffice *postOffice;

//Project3 part3
KernelServerLock ServerLockTable[MAXLOCKNUM];
KernelServerCondition ServerContitionTable[MAXCVNUM];

KernelMV MVTable[MAXMVNUM];
struct Socket* receivedSocketTable[MAXCLINETNUM][MAXMAILBOXNUM];

//Project4
int processMailbox;		//Allocate unique mailbox number for each thread
Lock *processMailboxLock;
bool bullyOn;
int serverSize;
int machineID;
#endif


// External definition, to allow us to take a pointer to this function
extern void Cleanup();


//----------------------------------------------------------------------
// TimerInterruptHandler
// 	Interrupt handler for the timer device.  The timer device is
//	set up to interrupt the CPU periodically (once every TimerTicks).
//	This routine is called each time there is a timer interrupt,
//	with interrupts disabled.
//
//	Note that instead of calling Yield() directly (which would
//	suspend the interrupt handler, not the interrupted thread
//	which is what we wanted to context switch), we set a flag
//	so that once the interrupt handler is done, it will appear as 
//	if the interrupted thread called Yield at the point it is 
//	was interrupted.
//
//	"dummy" is because every interrupt handler takes one argument,
//		whether it needs it or not.
//----------------------------------------------------------------------
static void
TimerInterruptHandler(int dummy)
{
    if (interrupt->getStatus() != IdleMode)
	interrupt->YieldOnReturn();
}

//----------------------------------------------------------------------
// Initialize
// 	Initialize Nachos global data structures.  Interpret command
//	line arguments in order to determine flags for the initialization.  
// 
//	"argc" is the number of command line arguments (including the name
//		of the command) -- ex: "nachos -d +" -> argc = 3 
//	"argv" is an array of strings, one for each command line argument
//		ex: "nachos -d +" -> argv = {"nachos", "-d", "+"}
//----------------------------------------------------------------------
void
Initialize(int argc, char **argv)
{
    int argCount;
    char* debugArgs = "";
    bool randomYield = FALSE;

#ifdef USER_PROGRAM
    bool debugUserProg = FALSE;	// single step user program
	pageReplaceType = FIFO;     //Default type is FIFO
#endif
#ifdef FILESYS_NEEDED
    bool format = FALSE;	// format disk
#endif
#ifdef NETWORK
    double rely = 1;		// network reliability
    int netname = 0;		// UNIX socket name
#endif
    
    for (argc--, argv++; argc > 0; argc -= argCount, argv += argCount) {
	argCount = 1;
	if (!strcmp(*argv, "-d")) {
	    if (argc == 1)
		debugArgs = "+";	// turn on all debug flags
	    else {
	    	debugArgs = *(argv + 1);
	    	argCount = 2;
	    }
	} else if (!strcmp(*argv, "-rs")) {
	    ASSERT(argc > 1);
	    RandomInit(atoi(*(argv + 1)));	// initialize pseudo-random
						// number generator
	    randomYield = TRUE;
	    argCount = 2;
	}
	

#ifdef USER_PROGRAM
	if (!strcmp(*argv, "-P")) {
		ASSERT(argc > 1);
		
		if(!strcmp(*(argv + 1),"RAND"))  {
			pageReplaceType = RANDOM;
		}
		argCount = 2;
	}
	
	if (!strcmp(*argv, "-s"))
	    debugUserProg = TRUE;
#endif
#ifdef FILESYS_NEEDED
	if (!strcmp(*argv, "-f"))
	    format = TRUE;
#endif
#ifdef NETWORK
	if (!strcmp(*argv, "-l")) {
	    ASSERT(argc > 1);
	    rely = atof(*(argv + 1));
	    argCount = 2;
	} else if (!strcmp(*argv, "-m")) {
	    ASSERT(argc > 1);
	    netname = atoi(*(argv + 1));
		machineID = netname;
		argCount = 2;
		
		serverSize = atoi(*(argv + 2));
	    argCount = 3;
		
		if(strcmp(*(argv + 3), "ON") ==0) bullyOn = true;
		else bullyOn = false;
		argCount = 4;
	}
#endif
    }

    DebugInit(debugArgs);			// initialize DEBUG messages
    stats = new Statistics();			// collect statistics
    interrupt = new Interrupt;			// start up interrupt handling
    scheduler = new Scheduler();		// initialize the ready queue
    if (randomYield)				// start the timer (if needed)
	timer = new Timer(TimerInterruptHandler, 0, randomYield);

    threadToBeDestroyed = NULL;

    // We didn't explicitly allocate the current thread we are running in.
    // But if it ever tries to give up the CPU, we better have a Thread
    // object to save its state. 
    currentThread = new Thread("main");		
    currentThread->setStatus(RUNNING);

    interrupt->Enable();
    CallOnUserAbort(Cleanup);			// if user hits ctl-C
    
#ifdef USER_PROGRAM
    machine = new Machine(debugUserProg);	// this must come first
											//Initialize the LockTable
	for(int i=0; i<MAXLOCKNUM; i++) {
		LockTable[i].lock = NULL;
		LockTable[i].procSpace = NULL;
		LockTable[i].inUse = false;	
	}
	LockTableLock = new Lock("LockTableLock");
	
	for(int i=0; i<MAXCVNUM; i++) {		//Initialize the CVTable
		CVTable[i].condition = NULL;
		CVTable[i].procSpace = NULL;
		CVTable[i].numOfWait = 0;	
	}
	CVTableLock = new Lock("CVTableLock");
	

	

	
	
	//Project2 part2
	PhysMap = new BitMap(NumPhysPages);
	PhysMapLock = new Lock("PhysMapLock");

	procTable = new ProcessTable(MAXPROCESSNUM);
	ProcessTableLock = new Lock("ProcessTableLock");
	
	//Project 3
	ipt = new IPTEntry[NumPhysPages];
	tlbCounter = 0;
	iptLock = new Lock("iptLock");
	if(fileSystem->Create("../vm/swap", PageSize*SWAPSIZE) == false){
		printf("Error: Unable to initializing swap file during system initializing\n");
		interrupt->Halt();
	}
	swapFile = fileSystem->Open("../vm/swap");
	swapMap = new BitMap(SWAPSIZE);
	swapLock = new Lock("swapLock");
	ppnFIFOList = new List();
#endif

#ifdef FILESYS
    synchDisk = new SynchDisk("DISK");
#endif

#ifdef FILESYS_NEEDED
    fileSystem = new FileSystem(format);
#endif

#ifdef NETWORK
    postOffice = new PostOffice(netname, rely, MAXMAILBOXNUM);  //Each nachos instance  have 60 mailbox
	
		//Project3 part3
	
	for(int i=0; i<MAXLOCKNUM; i++) {
		ServerLockTable[i].theServerLock = NULL;
		ServerLockTable[i].inUse = false;	
	}
	
	for(int i=0; i<MAXCVNUM; i++) {
		ServerContitionTable[i].theServerCondition = NULL;
		ServerContitionTable[i].numOfWait = 0;
	}

	for(int i=0; i<MAXMVNUM; i++) {		//Initialize the MVTable
		MVTable[i].mvArray = NULL;
		MVTable[i].mvName = NULL;
		MVTable[i].arraySize = 0;
	}
	
	for(int i=0; i<MAXCLINETNUM; i++) {
		for(int j=0; j<MAXMAILBOXNUM;j++) {
			receivedSocketTable[i][j] = NULL;
		}
	}
	
	//Project4
	processMailbox = 0;
	processMailboxLock = new Lock("processMailboxLock");
#endif
}

//----------------------------------------------------------------------
// Cleanup
// 	Nachos is halting.  De-allocate global data structures.
//----------------------------------------------------------------------
void
Cleanup()
{
    printf("\nCleaning up...\n");
#ifdef NETWORK
    delete postOffice;
#endif
    
#ifdef USER_PROGRAM
    delete machine;
	fileSystem->Remove("../vm/swap");
	delete swapFile;
	delete ppnFIFOList;
#endif

#ifdef FILESYS_NEEDED
    delete fileSystem;
#endif

#ifdef FILESYS
    delete synchDisk;
#endif
    
    delete timer;
    delete scheduler;
    delete interrupt;
    
    Exit(0);
}

